// includes, system
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "../glew/glew.h"
#include "../glut/glut.h"

#include "../cuCommon/cuError.h"
#include "../cuImage/cuImage.h"

#include <time.h>

cuImage img_1, img_2, img_mag, img_dir, img_src;

cuImage img_upscale, img_upscale_h, img_upscale_w;

void Display(void)
{
  glClearColor(0, 0, 0, 1);
  glClear(GL_COLOR_BUFFER_BIT);

  glBegin(GL_TRIANGLES);
  glTexCoord2f(0, 0); glVertex2f(-1, -1);
  glTexCoord2f(2, 0); glVertex2f(+3, -1);
  glTexCoord2f(0, 2); glVertex2f(-1, +3);
  glEnd();

  glutSwapBuffers();
}

void Idle(void)
{
  Display();
}

void Keyboard(unsigned char key, int mousex, int mousey)
{
  switch (key)
  {
    case 'q': 
    case 'Q':

      exit(0);
      break;

    default:
      break;
  }
}

void Reshape(int w, int h)
{
  glViewport(0, 0, w, h);
}

int main( int argc, char** argv) 
{
  glutInit( &argc, argv );  
  glutInitWindowSize(256, 256);  
  glutInitWindowPosition(0, 0);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

  glutCreateWindow("image processing template");

	cuImage::Init();

	char loadname[]                               = "../Media/textures/lighthouse.bmp";
	char savename_gamma[]                         = "../Media/textures/color/_gamma.bmp";
	char savename_hsv[]                           = "../Media/textures/color/_hsv.bmp";
	char savename_yuv[]                           = "../Media/textures/color/_yuv.bmp";
	char savename_negative[]                      = "../Media/textures/color/_negative.bmp";
	char savename_negative_negative[]             = "../Media/textures/color/_negative_negative.bmp";
	char savename_luminance[]                     = "../Media/textures/color/_luminance.bmp";

	char savename_boxblur[]                       = "../Media/textures/blur/_boxblur.bmp";
	char savename_convolution[]                   = "../Media/textures/blur/_convolution.bmp";
	char savename_convolution_smem[]              = "../Media/textures/blur/_convolution_smem.bmp";
	char savename_gaussian_2d[]                   = "../Media/textures/blur/_gaussian_2d.bmp";
	char savename_gaussian_sep[]                  = "../Media/textures/blur/_gaussian_sep.bmp";
	char savename_bilateral[]                     = "../Media/textures/blur/_bilateral.bmp";
	
	char savename_nonlocalmeans_tex[]            = "../Media/textures/nonlocalmeans/_tex.bmp";
	char savename_nonlocalmeans_tex_smem[]       = "../Media/textures/nonlocalmeans/_tex_smem.bmp";
	char savename_nonlocalmeans_gmem_smem[]      = "../Media/textures/nonlocalmeans/_gmem_smem.bmp";
	char savename_nonlocalmeans_tex_e3e3[]       = "../Media/textures/nonlocalmeans/_tex_e3e3.bmp";
	char savename_nonlocalmeans_tex_smem_e3e3[]  = "../Media/textures/nonlocalmeans/_tex_smem_e3e3.bmp";
	char savename_nonlocalmeans_gmem_smem_e3e3[] = "../Media/textures/nonlocalmeans/_gmem_smem_e3e3.bmp";
	char savename_nonlocalmeans_gmem_smem_e3e3_32x32[] = "../Media/textures/nonlocalmeans/_gmem_smem_e3e3_32x32.bmp";
	
	char savename_bilinear[]                     = "../Media/textures/upsampling/_bilinear.bmp";
	char savename_bicubic[]                      = "../Media/textures/upsampling/_bicubic.bmp";
	char savename_monotonebicubic[]              = "../Media/textures/upsampling/_monotonebicubic.bmp";
	char savename_hermitespline[]                = "../Media/textures/upsampling/_hermitespline.bmp";
	char savename_lanczos[]                      = "../Media/textures/upsampling/_lanczos.bmp";

	char savename_roberts[]                       = "../Media/textures/edge/_roberts.bmp";
	char savename_roberts_smem[]                  = "../Media/textures/edge/_roberts_smem.bmp";
	char savename_laplace[]                       = "../Media/textures/edge/_laplace.bmp";
	char savename_sobel[]                         = "../Media/textures/edge/_sobel.bmp";
	char savename_prewitt[]                       = "../Media/textures/edge/_prewitt.bmp";
	char savename_canny[]                         = "../Media/textures/edge/_canny.bmp";
	char savename_canny_blur[]                    = "../Media/textures/edge/_canny_blur.bmp";
	
	char savename_bilinear_h[]                    = "../Media/textures/upsampling_tmp/_bilinear_h.bmp";
	char savename_bicubic_h[]                     = "../Media/textures/upsampling_tmp/_bicubic_h.bmp";
	char savename_monotonebicubic_h[]             = "../Media/textures/upsampling_tmp/_monotonebicubic_h.bmp";
	char savename_hermitespline_h[]               = "../Media/textures/upsampling_tmp/_hermitespline_h.bmp";
	char savename_lanczos_h[]                     = "../Media/textures/upsampling_tmp/_lanczos_h.bmp";

	const int N = 10;

	// load image
	img_1.Load(loadname);

	// allocate cudaArray and pitch linear memory
	img_1.Create(BUFFER_A | BUFFER_D);

	// duplicate img_1 in img_2
	img_2 = img_1;

	// duplicate img_1 in img_mag & img_dir
	img_mag = img_1;
	img_dir = img_1;
	img_src = img_1;

	uint32 w = img_1.Width();
	uint32 h = img_1.Height();

	img_upscale.Create  (BUFFER_H | BUFFER_A | BUFFER_D, 2*w, 2*h);
	img_upscale_h.Create(BUFFER_H | BUFFER_A | BUFFER_D, 2*h,   w);
	
	// copy data from host memory to cudaArray
	img_1.Memcpy(BUFFER_H_TO_A);

	img_src.Memcpy(BUFFER_H_TO_D);
	img_src.Memcpy(BUFFER_H_TO_A);

	//////////////////////////////////////////////////////////////////////////
	/// Test Gamma correction
	//////////////////////////////////////////////////////////////////////////
	Gamma(img_1, img_1, 2.0f);

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_gamma);

	//////////////////////////////////////////////////////////////////////////
	/// Test Negative
	//////////////////////////////////////////////////////////////////////////
	Negative(img_2, img_1);

	// copy from pitch linear memory back to host memory
	img_2.Memcpy(BUFFER_D_TO_H);

	// save image
	img_2.Save(savename_negative);

	//////////////////////////////////////////////////////////////////////////
	/// Test Luminance
	//////////////////////////////////////////////////////////////////////////
	Luminance(img_1, img_1);

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_luminance);

	//////////////////////////////////////////////////////////////////////////
	/// Test RGB_to_HSV
	//////////////////////////////////////////////////////////////////////////
	RGB_to_HSV(img_1, img_1);

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_hsv);

	//////////////////////////////////////////////////////////////////////////
	/// Test RGB_to_YUV
	//////////////////////////////////////////////////////////////////////////
	RGB_to_YUV(img_1, img_1);

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_yuv);

	//////////////////////////////////////////////////////////////////////////
	/// Test BoxBlur
	//////////////////////////////////////////////////////////////////////////
	BoxBlur<5>(img_1, img_1);

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_boxblur);

	//////////////////////////////////////////////////////////////////////////
	/// Test Convolution2D
	//////////////////////////////////////////////////////////////////////////
	float boxblur[] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 
		                 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
										 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };

	clock_t start = clock();
	Convolution2D(img_1, img_1, boxblur, -7, 7, -7, 7);
	clock_t end = clock();

	std::cout << "Convolution 2D execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// copy from cudaArra memory back to device memory
	img_1.Memcpy(BUFFER_A_TO_D);

	// save image
	img_1.Save(savename_convolution);

	//////////////////////////////////////////////////////////////////////////
	/// Test Convolution2D_smem
	//////////////////////////////////////////////////////////////////////////
	start = clock();
	Convolution2D_smem(img_1, img_1, boxblur, -7, 7, -7, 7);
	end = clock();

	std::cout << "Convolution2D_smem execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_convolution_smem);

	//////////////////////////////////////////////////////////////////////////
	/// Test Gaussian
	//////////////////////////////////////////////////////////////////////////
	float sigma = 10.0f;
	float sigma_squared = (2 * sigma * sigma);
	int radius = 2 * sigma + 1.0f;
	int size = 2 * radius + 1;

	float * pGaussianKernel1D = new float[size];
	float * pGaussianKernel2D = new float[size*size];

	int i2d = 0;
	int i1d = 0;

	for (int ir = -radius; ir <= radius; ir++)
	{
		for (int ic = -radius; ic <= radius; ic++)
		{
			pGaussianKernel2D[i2d++] = exp( -((ir*ir) + (ic*ic)) /  sigma_squared);
		}
		
		pGaussianKernel1D[i1d++] = exp( -(ir*ir) /  sigma_squared);
	}
	
	// Run Gaussian 2D
	start = clock();
	Convolution2D( img_1, img_1, pGaussianKernel2D, -radius, radius, -radius, radius);
	end = clock();

	std::cout << "Convolution2D with Gaussian kernel execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_gaussian_2d);

	// Run Gaussian Separable
	start = clock();
	ConvolutionX( img_2, img_1, pGaussianKernel1D, -radius, radius);
	end = clock();

	std::cout << "Separable Convolution X with Gaussian kernel execution time: " << end - start << std::endl;

	img_2.Memcpy(BUFFER_D_TO_A);

	start = clock();
	ConvolutionY( img_1, img_2, pGaussianKernel1D, -radius, radius);
	end = clock();

	std::cout << "Separable Convolution Y with Gaussian kernel execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_gaussian_sep);

	delete [] pGaussianKernel1D;
	delete [] pGaussianKernel2D;

	//////////////////////////////////////////////////////////////////////////
	/// Test Bilateral
	//////////////////////////////////////////////////////////////////////////
	start = clock();
	Bilateral2D(img_1, img_1, 3, 0.1f, 0.0f);
	end = clock();

	std::cout << "Bilateral execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_bilateral);

	//////////////////////////////////////////////////////////////////////////
	/// Test NonLocalMeans
	//////////////////////////////////////////////////////////////////////////
	float noise = 0.05f;
	float th = 0.0f;
	start = clock();
	for (int i = 0; i < N; i++)
		NonLocalMeans_tex(img_1, img_src, 3, 3, noise, th);	
	end = clock();

	std::cout << "NonLocalMeans_tex execution time: " << end - start << std::endl;

	img_1.Memcpy(BUFFER_D_TO_H);
	img_1.Save(savename_nonlocalmeans_tex);
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	for (int i = 0; i < N; i++)
		NonLocalMeans_tex<3, 3>(img_1, img_src, noise, th);
	end = clock();

	std::cout << "NonLocalMeans_tex<r,w> execution time: " << end - start << std::endl;

	img_1.Memcpy(BUFFER_D_TO_H);
	img_1.Save(savename_nonlocalmeans_tex_e3e3);
	img_1.Memset(BUFFER_H | BUFFER_D);

	//////////////////////////////////////////////////////////////////////////
	/// Test NonLocalMeans_smem
	//////////////////////////////////////////////////////////////////////////
	img_1.Memset(BUFFER_D | BUFFER_H);

	start = clock();
	for (int i = 0; i < N; i++)
		NonLocalMeans_tex_smem(img_1, img_src, 3, 3, noise, th);
	end = clock();

	std::cout << "NonLocalMeans_tex_smem execution time: " << end - start << std::endl;

	img_1.Memcpy(BUFFER_D_TO_H);
	img_1.Save(savename_nonlocalmeans_tex_smem);
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	for (int i = 0; i < N; i++)
		NonLocalMeans_tex_smem<3, 3>(img_1, img_src, noise, th);
	end = clock();

	std::cout << "NonLocalMeans_tex_smem<r,w> execution time: " << end - start << std::endl;

	img_1.Memcpy(BUFFER_D_TO_H);
	img_1.Save(savename_nonlocalmeans_tex_smem_e3e3);
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	for (int i = 0; i < N; i++)
		NonLocalMeans_gmem_smem(img_1, img_src, 3, 3, noise, th);
	end = clock();

	std::cout << "NonLocalMeans_gmem_smem execution time: " << end - start << std::endl;

	img_1.Memcpy(BUFFER_D_TO_H);
	img_1.Save(savename_nonlocalmeans_gmem_smem);
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
		for (int i = 0; i < N; i++)
	NonLocalMeans_gmem_smem<3, 3>(img_1, img_src, noise, th);
	end = clock();

	std::cout << "NonLocalMeans_gmem_smem<r,w> execution time: " << end - start << std::endl;

	img_1.Memcpy(BUFFER_D_TO_H);
	img_1.Save(savename_nonlocalmeans_gmem_smem_e3e3);
	img_1.Memset(BUFFER_H | BUFFER_D);

	//////////////////////////////////////////////////////////////////////////
	/// Test Bilinear
	//////////////////////////////////////////////////////////////////////////
	img_upscale_h.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Bilinear(img_upscale, img_1, img_upscale_h);
	end = clock();

	std::cout << "Bilinear execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_upscale.Memcpy(BUFFER_D_TO_H);
	img_upscale_h.Memcpy(BUFFER_D_TO_H);

	// save image
	img_upscale.Save(savename_bilinear);
	img_upscale_h.Save(savename_bilinear_h);

	//////////////////////////////////////////////////////////////////////////
	/// Test Bicubic
	//////////////////////////////////////////////////////////////////////////
	img_upscale_h.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Bicubic(img_upscale, img_1, img_upscale_h, 1.2f);
	end = clock();

	std::cout << "Bicubic execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_upscale.Memcpy(BUFFER_D_TO_H);
	img_upscale_h.Memcpy(BUFFER_D_TO_H);

	// save image
	img_upscale.Save(savename_bicubic);
	img_upscale_h.Save(savename_bicubic_h);

	//////////////////////////////////////////////////////////////////////////
	/// Test HermiteSpline Cardinal tension eq 0.5
	//////////////////////////////////////////////////////////////////////////
	img_upscale_h.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	HermiteSpline(img_upscale, img_1, img_upscale_h, 0.5f);
	end = clock();

	std::cout << "HermiteSpline execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_upscale.Memcpy(BUFFER_D_TO_H);
	img_upscale_h.Memcpy(BUFFER_D_TO_H);

	// save image
	img_upscale.Save(savename_hermitespline);
	img_upscale_h.Save(savename_hermitespline_h);

	//////////////////////////////////////////////////////////////////////////
	/// Test MonotoneBicubic
	//////////////////////////////////////////////////////////////////////////
	img_upscale_h.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	MonotoneBicubic(img_upscale, img_1, img_upscale_h);
	end = clock();

	std::cout << "MonotoneBicubic execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_upscale.Memcpy(BUFFER_D_TO_H);
	img_upscale_h.Memcpy(BUFFER_D_TO_H);

	// save image
	img_upscale.Save(savename_monotonebicubic);
	img_upscale_h.Save(savename_monotonebicubic_h);

	//////////////////////////////////////////////////////////////////////////
	/// Test Lanczos
	//////////////////////////////////////////////////////////////////////////
	img_upscale_h.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Lanczos(img_upscale, img_1, img_upscale_h, 3, 1.2f);
	end = clock();

	std::cout << "Lanczos execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_upscale.Memcpy(BUFFER_D_TO_H);
	img_upscale_h.Memcpy(BUFFER_D_TO_H);

	img_upscale_h.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Lanczos<3>(img_upscale, img_1, img_upscale_h, 1.2f);
	end = clock();

	std::cout << "Lanczos<R> execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_upscale.Memcpy(BUFFER_D_TO_H);
	img_upscale_h.Memcpy(BUFFER_D_TO_H);

	// save image
	img_upscale.Save(savename_lanczos);
	img_upscale_h.Save(savename_lanczos_h);

	//////////////////////////////////////////////////////////////////////////
	/// Test Roberts
	//////////////////////////////////////////////////////////////////////////
	start = clock();
	Roberts(img_1, img_1);
	end = clock();

	std::cout << "Roberts execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_roberts);

	//////////////////////////////////////////////////////////////////////////
	/// Test Roberts_smem
	//////////////////////////////////////////////////////////////////////////
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Roberts_smem(img_1, img_1);
	end = clock();

	std::cout << "Roberts_smem execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_roberts_smem);

	//////////////////////////////////////////////////////////////////////////
	/// Test Laplace
	//////////////////////////////////////////////////////////////////////////
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Laplace(img_1, img_1);
	end = clock();

	std::cout << "Laplace execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_laplace);

	//////////////////////////////////////////////////////////////////////////
	/// Test Prewitt
	//////////////////////////////////////////////////////////////////////////
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Prewitt(img_1, img_1);
	end = clock();

	std::cout << "Prewitt execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_prewitt);

	//////////////////////////////////////////////////////////////////////////
	/// Test Sobel
	//////////////////////////////////////////////////////////////////////////
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Sobel(img_1, img_1);
	end = clock();

	std::cout << "Sobel execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_sobel);

	//////////////////////////////////////////////////////////////////////////
	/// Test Canny
	//////////////////////////////////////////////////////////////////////////
	img_1.Memset(BUFFER_H | BUFFER_D);

	start = clock();
	Canny(img_1, img_1, img_2, img_mag, img_dir, 2, 0.1f, 0.5f);
	end = clock();

	std::cout << "Canny execution time: " << end - start << std::endl;

	// copy from pitch linear memory back to host memory
	img_1.Memcpy(BUFFER_D_TO_H);
	img_2.Memcpy(BUFFER_D_TO_H);

	// save image
	img_1.Save(savename_canny);
	img_2.Save(savename_canny_blur);

  glutDisplayFunc(Display);
  glutIdleFunc(Idle);
  glutKeyboardFunc(Keyboard);
  glutReshapeFunc(Reshape);

  //glutMainLoop();

  return 0;
}